﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using MkLink;
using SymbolicLinkManageCore.Exceptions;
using SymbolicLinkManageUI.Library;
using UtilityToolsCollect.ExtensionMethodLibrary;
using UtilityToolsCollectInStandard;

namespace SymbolicLinkManageCore;

public class ManageCore
{
    public static Window? MainWindow { get; set; }
    public static ILogManage? LogManage { get; set; }
    public static RecordData[] ScanComputer(CancellationToken token, string[]? ScanRoots = null, bool IsEnableHardLink = false, Action<float>? ProgressValueAsync = null)
    {
        string[][] m_result = new string[ScanRoots == null ? MkLinkTools.LocalPathRoots.Length : ScanRoots.Length][];
        EnumerationOptions enumerationOptions = new()
        {
            AttributesToSkip = FileAttributes.System | FileAttributes.Offline | FileAttributes.Temporary | FileAttributes.IntegrityStream,
            BufferSize = 4_194_304,
            IgnoreInaccessible = true,
            MatchCasing = MatchCasing.CaseInsensitive,
            RecurseSubdirectories = true,
            ReturnSpecialDirectories = false
        };
        int count = 0;

        try
        {
            Parallel.For(0, m_result.Length, new ParallelOptions { CancellationToken = token, MaxDegreeOfParallelism = m_result.Length < Environment.ProcessorCount ? m_result.Length : Environment.ProcessorCount }, i =>
            {
                m_result[i] = Directory.GetFileSystemEntries(ScanRoots == null ? MkLinkTools.LocalPathRoots[i] : ScanRoots[i], "*", enumerationOptions);
                ProgressValueAsync?.Invoke(Interlocked.Increment(ref count) / (float)m_result.Length * 100);
            });
            count = 0;
            //string[] Merge = m_result.ArrayMerge();
            float TotalCount = m_result.Sum(p => p.Length);

            int TimeCount = 0;
            return m_result.SelectMany(p => p).AsParallel().WithCancellation(token).WithExecutionMode(ParallelExecutionMode.ForceParallelism).WithDegreeOfParallelism(Environment.ProcessorCount)
                .Where(p =>
                {
                    Interlocked.Increment(ref count);
                    try
                    {
                        if (count > TimeCount)
                        {
                            ProgressValueAsync?.Invoke(count / TotalCount * 100);
                            TimeCount = count + 1000;
                        }
                        if (IsEnableHardLink)
                            return MkLinkTools.GetLinkType(p, false, true).IsSupportLinkType();
                        else
                            return (File.GetAttributes(p) & FileAttributes.ReparsePoint) > 0;
                    }
                    catch (Exception exc)
                    {
                        LogManage?.LogWriteExceptionInfo("文件[{p}]:获取属性错误:", exc);
                        return false;
                    }
                }).Select(p => RecordData.Create(p, out RecordData data, out _) ? data : RecordData.DefaultEmpty).Where(p => p != RecordData.DefaultEmpty).ToArray();
        }
        catch (OperationCanceledException) { return []; }
        catch (Exception exc) { LogManage?.LogWriteExceptionInfo("扫描磁盘异常:", exc, true); return []; }
        finally 
        {
            for (int i = 0; i < m_result.Length; i++)
            {
                Array.Clear(m_result[i]);
                m_result[i] = null!;
            }
            GC.Collect();
        }
    }

    public static RecordData[] ReadPathsFormTxtFile(string m_FilePath)
    {
        if (!File.Exists(m_FilePath))
        {
            MessageBoxEx.Show(MainWindow, "文件不存在", "读取文件错误", MessageBoxButton.OK,MessageBoxImage.Error);
            return [];
        }
        return File.ReadAllLines(m_FilePath).Where(p =>
        {
            try
            {
                return MkLinkTools.GetLinkType(p).IsSupportLinkType();
            }
            catch (Exception exc)
            {
                LogManage?.LogWriteExceptionInfo("文件[{p}]:获取属性错误:", exc);
                return false;
            }
        }).Select(p => new RecordData(p)).ToArray();
    }

    public static bool CreateLink(LinkType linkType, string LinkPoint, string LinkTargetPath, bool ForceCreateDirectory = false, bool EnableOverwrite = false) =>
        CreateLink(linkType, PathInfo.GetPathType(LinkTargetPath), LinkPoint, LinkTargetPath, ForceCreateDirectory, EnableOverwrite);

    public static bool CreateLink(LinkType linkType, PathType pathType ,string LinkPoint, string LinkTargetPath, bool ForceCreateDirectory = false, bool EnableOverwrite = false)
    {
        if (!linkType.IsSupportLinkType())
            return false;
        try
        {
            if (linkType == LinkType.SYMLINK || linkType == LinkType.SYMLINKD)
                linkType = pathType == PathType.FileType ? LinkType.SYMLINK : (pathType == PathType.DirectoryType ? LinkType.SYMLINKD : LinkType.None);

            MkLinkTools.CreateLink(linkType, pathType, LinkPoint, LinkTargetPath, ForceCreateDirectory, EnableOverwrite);
            if (linkType == LinkType.HardLink)
                AddHardLinkRecordData(LinkPoint);
            else
                AddRecordData(new RecordData(LinkPoint));

            return true;
        }
        catch (Exception exc)
        {
            MessageBoxEx.Show(MainWindow, exc.Message, "创建连接错误", MessageBoxButton.OK, MessageBoxImage.Error);
            LogManage?.LogWriteExceptionInfo($"向 [{LinkPoint}] 创建连接 [{linkType}] ,错误,目标：[{LinkTargetPath}]", exc);
            return false;
        }
    }

    public static RecordData CreateLinkAndRecordData(LinkType linkType, string LinkPoint, string LinkTargetPath, bool ForceCreateDirectory = false, bool EnableOverwrite = false)
    {
        try
        {
            MkLinkTools.CreateLink(linkType, LinkPoint, LinkTargetPath, ForceCreateDirectory, EnableOverwrite);
            return new RecordData(LinkPoint, linkType);
        }
        catch (Exception exc)
        {
            MessageBoxEx.Show(MainWindow, exc.Message, "创建连接错误", MessageBoxButton.OK, MessageBoxImage.Error);
            LogManage?.LogWriteExceptionInfo($"向 [{LinkPoint}] 创建连接 [{linkType}] ,错误", exc);
            return RecordData.DefaultEmpty;
        }
    }

    public static bool GetHardLinkRecordData(string[] Paths, out RecordData? Result)
    {
        Result = null;
        if (Paths == null || Paths.Length < 1)
            return false;

        IEnumerable<RecordData> HardLinkData = Config.Current.RecordDatas.Where(p => p.linkType == LinkType.HardLink);
        foreach (string item in Paths)
        {
            foreach (RecordData data in HardLinkData)
            {
                if (data.HardLinkGroup != null && data.HardLinkGroupContains(item))
                {
                    Result = data;
                    return true;
                }
            }
        }
        return false;
    }
    public static void AddHardLinkRecordData(string LinkPoint)
    {
        if (GetHardLinkRecordData(MkLinkTools.GetFileHardLinks_Asterisk(LinkPoint), out RecordData? Hit))
        {
            Hit!.AddHardLinkData(LinkPoint);
            Hit.HardLinkGroupSort();
        }
        else
            Config.Add(new RecordData(LinkPoint, LinkType.HardLink));
        Config.Save();
    }
    public static void AddHardLinkRecordData(RecordData? recordData)
    {
        if (recordData == null)
            return;
        if (recordData.linkType != LinkType.HardLink)
            throw new ArgumentException($"添加硬链接记录时，参数[{nameof(recordData)}],不是硬链接类型数据");
        if (!recordData.IsHardLinkGroupExists)
            return;
        if(GetHardLinkRecordData(recordData.HardLinkGroup!.Select(p=>p.FilePath).ToArray(),out RecordData? Hit))
        {
            foreach (HardLinkData item in recordData.HardLinkGroup!)
                Hit!.AddHardLinkData(item);
            Hit!.HardLinkGroupSort();
        }
        else Config.Add(recordData);
        Config.Save();
    }
    public static void AddRecordData(string LinkPoint)
    {
        LinkType linkType = MkLinkTools.GetLinkType(LinkPoint);
        if (!linkType.IsSupportLinkType())
            throw new NotSupportedException($"选择的连接点 [{LinkPoint}] 不是支持的类型");
        if (linkType == LinkType.HardLink)
        {
            AddHardLinkRecordData(LinkPoint);
            return;
        }
        RecordData recordData = new RecordData(LinkPoint, linkType);
        AddRecordData(recordData);
    }
    public static void AddRecordData(RecordData LinkPoint)
    {
        LinkPoint.IsEnableTargetPathModeShowUI = Config.Current.RecordDatas.FirstOrDefault()?.IsEnableTargetPathModeShowUI ?? false;
        if (LinkPoint.linkType == LinkType.HardLink)
        {
            AddHardLinkRecordData(LinkPoint);
            return;
        }
        else if (Config.Current.RecordDatas.Where(p => p.linkType != LinkType.HardLink).Any(p => p.LinkPointPath.Equals(LinkPoint.LinkPointPath, StringComparison.OrdinalIgnoreCase)))
            return;

        Config.Add(LinkPoint);
        Config.Save();
    }

    //从value获取不在Source中存在的对象
    private static IEnumerable<RecordData> Except(IEnumerable<RecordData> Source, IEnumerable<RecordData> value)
    {
        IEnumerable<IGrouping<bool, RecordData>>? SourceGroup = Source.GroupBy(p => p.linkType == LinkType.HardLink);
        IEnumerable<IGrouping<bool, RecordData>>? valueGroup = value.GroupBy(p => p.linkType == LinkType.HardLink);

        RecordData[] SourceNoHardLink = SourceGroup.FirstOrDefault(p => !p.Key)?.ToArray() ?? [];
        RecordData[] SourceHardLink = SourceGroup.FirstOrDefault(p => p.Key)?.ToArray() ?? [];

        RecordData[] valueNoHardLink = valueGroup.FirstOrDefault(p => !p.Key)?.ToArray() ?? [];
        RecordData[] valueHardLink = valueGroup.FirstOrDefault(p => p.Key)?.ToArray() ?? [];

        HashSet<string> set = new(SourceNoHardLink.Select(p => p.LinkPointPath), StringComparer.OrdinalIgnoreCase);

        foreach (RecordData item in valueNoHardLink)
        {
            if (set.Add(item.LinkPointPath))
                yield return item;
        }
        foreach (RecordData item in valueHardLink)
        {
            if (!SourceHardLink.Any(p => RecordData.EqualsHardLinkGroup(p.HardLinkGroup, item.HardLinkGroup)))
                yield return item;
        }
    }

    //针对硬链接，为了保持数据正确，通过TestValue的数据，从Source取出与其相关的对象,如果存在相关对象，将对相关对象合并TestValue后进行重构，并返回源对象与被重构后并除重的对象的集合
    private readonly record struct RefactorHardLinkData(RecordData[] Source, RecordData[] Refactor);
    private static Task<(bool TestResult, RefactorHardLinkData RefactorHardLinkData)> TestAndRefactorCorrelation(IEnumerable<RecordData> Source, IEnumerable<RecordData> TestValue) => Task.Run(() =>
    {
        RefactorHardLinkData RefactorData = default;
        IEnumerable<(RecordData Source, RecordData Reference)> Intersection = Source.Where(p => p.linkType == LinkType.HardLink).GetHardLinkIntersection(TestValue.Where(p => p.linkType == LinkType.HardLink));
        if (!Intersection.Any())
            return (false, RefactorData);

        HashSet<string> set = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

        TestValue.SelectMany(p => p.HardLinkGroup ?? []).Select(p => p.FilePath).ForEach(p => set.Add(p));
        foreach ((RecordData Source, RecordData Reference) item in Intersection)
        {
            if (item.Source.IsHardLinkGroupExists)
                foreach (HardLinkData s in item.Source.HardLinkGroup!)
                    set.Add(s.FilePath);
        }

        List<RecordData> Refactor = new();
        foreach (string item in set.Where(p=>Path.Exists(p)))
            Refactor.Add(new RecordData(item, LinkType.HardLink));
        RefactorData = new RefactorHardLinkData(Intersection.Select(p => p.Source).ToArray(), Refactor.GetRecordDataDistinct().ToArray());
        RefactorData.Refactor.ForEach(p => p.HardLinkGroupSort());
        return (true, RefactorData);
    });
    public static async Task AddRecordDatas(RecordData[] records)
    {
        bool IsEnableTargetPathModeShowUI = Config.Current.RecordDatas.FirstOrDefault()?.IsEnableTargetPathModeShowUI ?? false;

        RecordData[] SourcesData = Config.Current.RecordDatas.Where(s => s.linkType != LinkType.HardLink).ToArray();
        IEnumerable<IGrouping<bool, RecordData>> group = records.GroupBy(p => p.linkType == LinkType.HardLink);
        IEnumerable<RecordData> Standard = group.FirstOrDefault(p => !p.Key)?.Select(p => p) ?? [];
        IEnumerable<RecordData> HardLink = group.FirstOrDefault(p => p.Key)?.Select(p => p) ?? [];

        //非硬链接
        Standard.Where(p => !SourcesData.Any(s => s.LinkPointPath.Equals(p.LinkPointPath, StringComparison.OrdinalIgnoreCase))).ForEach(recordData => 
            {
                recordData.IsEnableTargetPathModeShowUI = IsEnableTargetPathModeShowUI;
                Config.Add(recordData);
            });
        
        //处理硬链接
        SourcesData = Config.Current.RecordDatas.Where(s => s.linkType == LinkType.HardLink).ToArray();
        (bool TestResult, RefactorHardLinkData RefactorHardLinkData) CorrelationTest = await TestAndRefactorCorrelation(SourcesData, HardLink);
        if (CorrelationTest.TestResult)
        {
            //测试结果有重，清除原来的，加入重构的
            foreach (RecordData item in CorrelationTest.RefactorHardLinkData.Source)
                Config.Remove(item);
            foreach (RecordData item in CorrelationTest.RefactorHardLinkData.Refactor)
                Config.Add(item);
        }
        else
        {
            //测试无重，直接加
            foreach (RecordData recordData in HardLink)
                Config.Add(recordData);
        }
        Config.Current.RecordDatas.Where(p => p.linkType == LinkType.HardLink).ForEach(p => p.HardLinkGroupSort());
        Config.Save();
    }

    public static async Task<(RecordData[] HardLinkData, RecordData[] result)> HardLinkDataRefactor(IEnumerable<RecordData> HardLinkData) => await Task.Run(() =>
    {
        HardLinkData = HardLinkData.Where(p => p.linkType == LinkType.HardLink);
        IEnumerable<RecordData> result = HardLinkData.SelectMany(p => p.HardLinkGroup ?? []).Select(p => p.FilePath).Distinct(EqualityComparer<string>.Default).Where(p=>Path.Exists(p)).Select(p => new RecordData(p, LinkType.HardLink)).GetRecordDataDistinct();
        result.ForEach(p => p.HardLinkGroupSort());
        return (HardLinkData.ToArray(), result.ToArray());
    });
    
    public static void Master_Slave_ShowSwitch()
    {
        bool IsEnableTargetPathModeShowUI = (!Config.Current.RecordDatas.FirstOrDefault()?.IsEnableTargetPathModeShowUI) ?? false;
        Config.Current.RecordDatas.ForEach(p => p.IsEnableTargetPathModeShowUI = IsEnableTargetPathModeShowUI);

    }

    public static void RemoveDuplicates(IEnumerable<RecordData> Source)
    {
        foreach (RecordData item in GetDuplicates(Source))
            Config.Remove(item);
        Config.Save();
    }
    public static IEnumerable<RecordData> GetDuplicates(IEnumerable<RecordData> Source)
    {
        IEnumerable<IGrouping<bool, RecordData>> Group = Source.GroupBy(p => p.linkType == LinkType.HardLink);
        RecordData[] datas = Group.FirstOrDefault(p => !p.Key)?.GetDuplicates(p => p.LinkPointPath).ToArray() ?? [];
        foreach (RecordData item in datas)
            yield return item;

        datas = Group.FirstOrDefault(p => p.Key)?.GetHardLinkGroupDuplicates().ToArray() ?? [];
        foreach (RecordData item in datas)
            yield return item;
    }
}
